<?php

namespace Kunfeng\Dify\Client\Impl;

use GuzzleHttp\Client;
use Kunfeng\Dify\Callback\WorkflowStreamCallback;
use Kunfeng\Dify\Client\DifyWorkflowClientInterface;
use Kunfeng\Dify\Constants\ApiPaths;
use Kunfeng\Dify\Enum\ResponseMode;
use Kunfeng\Dify\Event\EventFactory;
use Kunfeng\Dify\Exception\DifyApiException;
use Kunfeng\Dify\Model\Workflow\WorkflowRunRequest;
use Kunfeng\Dify\Model\Workflow\WorkflowRunResponse;
use Kunfeng\Dify\Model\Workflow\WorkflowRunStatusResponse;
use Kunfeng\Dify\Model\Workflow\WorkflowStopResponse;
use Kunfeng\Dify\Model\Workflow\WorkflowLogsResponse;
use Kunfeng\Dify\Model\Common\SimpleResponse;
use Kunfeng\Dify\Model\File\FileUploadRequest;
use Kunfeng\Dify\Model\File\FileUploadResponse;
use Kunfeng\Dify\Model\File\FilePreviewResponse;
use Kunfeng\Dify\Model\Chat\AppInfoResponse;
use Kunfeng\Dify\Model\Chat\AppParametersResponse;
use Kunfeng\Dify\Model\Chat\AppWebAppSettingResponse;

/**
 * Dify 工作流应用客户端实现
 */
class DifyWorkflowClient extends AbstractDifyClient implements DifyWorkflowClientInterface
{
    public function __construct(string $baseUrl, string $apiKey, $httpClient = null, $retryHandler = null)
    {
        parent::__construct($baseUrl, $apiKey, $httpClient, $retryHandler);
    }

    /**
     * 运行工作流（阻塞模式）
     */
    public function runWorkflow(WorkflowRunRequest $request): WorkflowRunResponse
    {
        $data = $this->executePost(ApiPaths::WORKFLOW_RUN, $request->toArray());
        return WorkflowRunResponse::fromArray($data);
    }

    /**
     * 运行工作流（流式模式）
     */
    public function runWorkflowStream(WorkflowRunRequest $request, WorkflowStreamCallback $callback): void
    {
        // 确保请求模式为流式
        $requestData = $request->toArray();
        $requestData['response_mode'] = ResponseMode::STREAMING;

        try {
            $response = $this->httpClient->request('POST', $this->baseUrl . ApiPaths::WORKFLOW_RUN, [
                'json' => $requestData,
                'stream' => true,
                'headers' => [
                    'Accept' => 'text/event-stream',
                ]
            ]);

            $this->processStreamResponse($response, $callback);
        } catch (\GuzzleHttp\Exception\GuzzleException $e) {
            $callback->handleException($e);
        }
    }

    /**
     * 获取工作流运行状态
     */
    public function getWorkflowRunStatus(string $taskId, string $user): WorkflowRunStatusResponse
    {
        $data = $this->executeGet(ApiPaths::WORKFLOW_RUN_STATUS, [
            'task_id' => $taskId,
            'user' => $user
        ]);
        return WorkflowRunStatusResponse::fromArray($data);
    }

    /**
     * 停止工作流运行
     */
    public function stopWorkflowRun(string $taskId, string $user): WorkflowStopResponse
    {
        $data = $this->executePost(ApiPaths::WORKFLOW_RUN_STOP, [
            'task_id' => $taskId,
            'user' => $user
        ]);
        return WorkflowStopResponse::fromArray($data);
    }

    /**
     * 获取工作流运行日志
     */
    public function getWorkflowRunLogs(string $taskId, string $user): WorkflowLogsResponse
    {
        $data = $this->executeGet(ApiPaths::WORKFLOW_RUN_LOGS, [
            'task_id' => $taskId,
            'user' => $user
        ]);
        return WorkflowLogsResponse::fromArray($data);
    }

    /**
     * 上传文件
     */
    public function uploadFile($fileOrRequest, $filePathOrStream, ?string $user = null): FileUploadResponse
    {
        return parent::uploadFile($fileOrRequest, $filePathOrStream, $user);
    }

    /**
     * 获取应用信息
     */
    public function getAppInfo(): AppInfoResponse
    {
        $data = $this->executeGet(ApiPaths::META);
        return AppInfoResponse::fromArray($data);
    }

    /**
     * 获取应用参数
     */
    public function getAppParameters(): AppParametersResponse
    {
        $data = $this->executeGet(ApiPaths::META . '/parameters');
        return AppParametersResponse::fromArray($data);
    }

    /**
     * 获取应用 WebApp 设置
     */
    public function getAppWebAppSettings(): AppWebAppSettingResponse
    {
        $data = $this->executeGet(ApiPaths::META . '/webapp-settings');
        return AppWebAppSettingResponse::fromArray($data);
    }

    /**
     * 预览文件
     */
    public function previewFile(string $fileId, bool $asAttachment = false): FilePreviewResponse
    {
        $data = $this->executeGet(ApiPaths::FILES_PREVIEW . '/' . $fileId, [
            'as_attachment' => $asAttachment
        ]);
        return FilePreviewResponse::fromArray($data);
    }

    /**
     * 处理流式响应
     */
    private function processStreamResponse($response, WorkflowStreamCallback $callback): void
    {
        $body = $response->getBody();

        while (!$body->eof()) {
            $line = $body->readLine();

            if (empty(trim($line))) {
                continue;
            }

            if (strpos($line, 'data: ') === 0) {
                $data = substr($line, 6);
                $eventData = json_decode($data, true);

                if ($eventData !== null) {
                    $event = EventFactory::create($eventData['event'] ?? 'unknown', $eventData);
                    $this->dispatchEvent($event, $callback);
                }
            } elseif (strpos($line, 'event: ping') === 0) {
                $callback->handlePing(new \Kunfeng\Dify\Event\PingEvent());
            }
        }
    }

    /**
     * 分发事件
     */
    private function dispatchEvent($event, WorkflowStreamCallback $callback): void
    {
        switch (get_class($event)) {
            case \Kunfeng\Dify\Event\WorkflowStartedEvent::class:
                $callback->handleWorkflowStarted($event);
                break;
            case \Kunfeng\Dify\Event\WorkflowFinishedEvent::class:
                $callback->handleWorkflowFinished($event);
                break;
            case \Kunfeng\Dify\Event\NodeStartedEvent::class:
                $callback->handleNodeStarted($event);
                break;
            case \Kunfeng\Dify\Event\NodeFinishedEvent::class:
                $callback->handleNodeFinished($event);
                break;
            case \Kunfeng\Dify\Event\IterationStartedEvent::class:
                $callback->handleIterationStarted($event);
                break;
            case \Kunfeng\Dify\Event\IterationNextEvent::class:
                $callback->handleIterationNext($event);
                break;
            case \Kunfeng\Dify\Event\IterationCompletedEvent::class:
                $callback->handleIterationCompleted($event);
                break;
            case \Kunfeng\Dify\Event\LoopStartedEvent::class:
                $callback->handleLoopStarted($event);
                break;
            case \Kunfeng\Dify\Event\LoopNextEvent::class:
                $callback->handleLoopNext($event);
                break;
            case \Kunfeng\Dify\Event\LoopCompletedEvent::class:
                $callback->handleLoopCompleted($event);
                break;
            case \Kunfeng\Dify\Event\WorkflowTextChunkEvent::class:
                $callback->handleWorkflowTextChunk($event);
                break;
            case \Kunfeng\Dify\Event\ErrorEvent::class:
                $callback->handleError($event);
                break;
        }
    }
}
